package top.lemna.base.core.exception;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

public class RecordException extends  RuntimeException {

  private static final long serialVersionUID = 1L;
  private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

  public RecordException(String message) {
    super(message);
  }

  public String getMessage() {
    // Get this exception's message.
    String msg = super.getMessage();
    msg+=" ["+getFieldString(this)+"]";
    Throwable parent = this;
    Throwable child;
    // Look for nested exceptions.
    while ((child = getNestedException(parent)) != null) {
      // Get the child's message.
      String msg2 = child.getMessage();
      // If we found a message for the child exception,
      // we append it.
      if (msg2 != null) {
        if (msg != null) {
          msg += ": " + msg2;
        } else {
          msg = msg2;
        }
      }
      // Any nested RecordNotExistException will append its own
      // children, so we need to break out of here.
      if (child instanceof RecordException) {
        break;
      }
      parent = child;
    }
    // Return the completed message.
    return msg;
  }

  private static Throwable getNestedException(Throwable parent) {
    return parent.getCause();
  }

  private String getFieldString(Object model) {
    StringBuffer sb = new StringBuffer();
    // ReflectionUtils.
    Field[] field = model.getClass().getDeclaredFields(); // 获取实体类的所有属性，返回Field数组
    try {
      for (int j = 0; j < field.length; j++) { // 遍历所有属性
        String name = field[j].getName(); // 获取属性的名字
        name = name.substring(0, 1).toUpperCase() + name.substring(1); // 将属性的首字符大写，方便构造get，set方法

        Method method = findMethod(model.getClass(), "get" + name);
        if (null == method) {
          continue;
        }
        Object value = method.invoke(model);
        if (null == value) {
          continue;
        }
        sb.append(", ").append(name).append("=");

        String type = field[j].getGenericType().toString(); // 获取属性的类型
        if (type.equals("class java.util.Date")) {
          Date date = (Date) value;
          sb.append(SDF.format(date));
          continue;
        }
        if (type.equals("class java.time.LocalDate")) {
          LocalDate date = (LocalDate) value;
          sb.append(date.format(DateTimeFormatter.BASIC_ISO_DATE));
          continue;
        }
        sb.append(value);
      }
    } catch (Exception e) {
      throw new RuntimeException("获取异常属性列表时发生异常", e);
    }
    if(sb.length()>0)
    {
      return sb.substring(2);
    }
    return sb.toString();
  }


  @Nullable
  public static Method findMethod(Class<?> clazz, String name) {
    Assert.notNull(clazz, "Class must not be null");
    Assert.notNull(name, "Method name must not be null");
    Class<?> searchType = clazz;
    while (searchType != null) {
      Method[] methods = (searchType.isInterface() ? searchType.getMethods()
          : getDeclaredMethods(searchType, false));
      for (Method method : methods) {
        if (name.equals(method.getName())) {
          return method;
        }
      }
      searchType = searchType.getSuperclass();
    }
    return null;
  }

  private static Method[] getDeclaredMethods(Class<?> clazz, boolean defensive) {
    Assert.notNull(clazz, "Class must not be null");
    Method[] result = null;
    try {
      Method[] declaredMethods = clazz.getDeclaredMethods();
      List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
      if (defaultMethods != null) {
        result = new Method[declaredMethods.length + defaultMethods.size()];
        System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
        int index = declaredMethods.length;
        for (Method defaultMethod : defaultMethods) {
          result[index] = defaultMethod;
          index++;
        }
      } else {
        result = declaredMethods;
      }
    } catch (Throwable ex) {
      throw new IllegalStateException("Failed to introspect Class [" + clazz.getName()
          + "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
    }
    return (result.length == 0 || !defensive) ? result : result.clone();
  }

  @Nullable
  private static List<Method> findConcreteMethodsOnInterfaces(Class<?> clazz) {
    List<Method> result = null;
    for (Class<?> ifc : clazz.getInterfaces()) {
      for (Method ifcMethod : ifc.getMethods()) {
        if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
          if (result == null) {
            result = new ArrayList<>();
          }
          result.add(ifcMethod);
        }
      }
    }
    return result;
  }
}
